package com.hup.utils.io.easyexcel;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.util.ConverterUtils;
import lombok.extern.log4j.Log4j2;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author hugan
 * @date 2020/2/7
 */
@Log4j2
public class ExcelReaderUtil {

    public static <T> ArrayList<T> read(String fileName, ReadSheetHandler<T> readSheetHandler) {
        log.debug("fileName={}", fileName);
        ExcelReader excelReader = null;

        ArrayList<AbsSheetReader<T>> lstSheetReader = new ArrayList<>();
        try {
            excelReader = EasyExcel.read(fileName).build();
            List<ReadSheet> sheets = excelReader.excelExecutor().sheetList();

            ArrayList<ReadSheet> lstSheetToRead = new ArrayList<>();
            for (int i = 0; i < sheets.size(); i++) {
                ReadSheet sheet = sheets.get(i);
                AbsSheetReader<T> sheetReader = readSheetHandler.accept(i, sheet);
                if (sheetReader == null) {
                    continue;
                }
                sheetReader.setSourceSheet(sheet.getSheetName());
                sheet.setClazz(sheetReader.getRowClass());
                sheet.setCustomReadListenerList(new ArrayList<>(1));
                sheet.getCustomReadListenerList().add(sheetReader);
                lstSheetToRead.add(sheet);

                lstSheetReader.add(sheetReader);
            }
            //触发读取数据到reader中
            excelReader.read(lstSheetToRead);

            //根据reader结果构造res
            ArrayList<T> res = new ArrayList<>();
            for (AbsSheetReader<T> reader : lstSheetReader) {
                res.addAll(reader.lstData);
            }
            return res;
        } finally {
            EExcelUtil.closeSilent(excelReader);
        }
    }

    public interface ReadSheetHandler<T> {
        /**
         * @param index 当前sheet的index,起始于0
         * @param readSheet 当前的sheet的name
         * @return null-不读取此sheet;notNull-读取此sheet
         */
        AbsSheetReader<T> accept(int index, ReadSheet readSheet);
    }

    public static abstract class AbsSheetReader<T> extends AnalysisEventListener<T> {
        protected ArrayList<T> lstData;
        protected String sourceSheet;

        public AbsSheetReader() {
            lstData = new ArrayList<>();
        }

        public void setSourceSheet(String sourceSheet) {
            this.sourceSheet = sourceSheet;
        }

        public abstract Class<T> getRowClass();

        /**
         * 校验表头头部,必须校验
         */
        private void validateHead(Map<Integer, String> headMap) {
            ExcelIgnoreUnannotated excelIgnoreUnannotated = getRowClass().getAnnotation(ExcelIgnoreUnannotated.class);
            if (excelIgnoreUnannotated == null) {
                //实现ExcelIgnoreUnannotated后,下面就不会校验没有ExcelProperty的属性
                throw new IllegalArgumentException(getRowClass() + "没有实现ExcelIgnoreUnannotated?");
            }
            for (Field field : getRowClass().getFields()) {
                ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
                log.debug("field={}, excelProperty={}", field, excelProperty);
                if (excelProperty == null) continue;//不校验没注解的属性

                ExcelPropertyExtend excelPropertyExtend = field.getAnnotation(ExcelPropertyExtend.class);
                if (excelPropertyExtend != null && excelPropertyExtend.allowMissing()) {
                    continue;
                }
                String needColName = excelProperty.value()[0];
                if (needColName != null && !needColName.isEmpty()) {
                    if (!headMap.containsValue(needColName)) {
                        throw new IllegalArgumentException("表格中没有[" + needColName + "]列");
                    }
                } else {
                    int needColIndex = excelProperty.index();
                    if (needColIndex == -1) {
                        throw new IllegalArgumentException(getRowClass() + ".ExcelProperty注解没有配置value和index!");
                    }
                    if (!headMap.containsKey(needColIndex)) {
                        throw new IllegalArgumentException("表格中没有[第" + needColIndex + "列]");
                    }
                }
            }
        }

        @Override
        public final void invokeHead(Map<Integer, CellData> headMap, AnalysisContext context) {
            //重新方法为final,强制校验表格表头
            Map<Integer, String> map = ConverterUtils.convertToStringMap(headMap, context);
            validateHead(map);
            invokeHeadMap(map, context);
        }

        @Override
        public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        }

        @Override
        public void invoke(T data, AnalysisContext context) {
            lstData.add(data);
        }

        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            log.debug("sheetName={}, 总行数={}", sourceSheet, lstData.size());
        }

        @Override
        public void onException(Exception exception, AnalysisContext context) throws Exception {
            super.onException(exception, context);
        }

    }

}